#!/usr/bin/env python
#-*- coding:utf-8 -*-

# import numpy as np
from displayer.displayer import PyplotDisplayer
from graph.graph import Graph
from matplotlib import image as img
from common.node import Node
# from PIL import Image as img

class PixelGraph(Graph):
    def __init__(self, *args, **kws):
        self.init(*args, **kws)

    def init(self, *args, **kws):
        pass
    
    def pixel(self, pos):
        if not self.check_pos(pos):
            return float('inf')
        return self.pixels[pos[0]][pos[1]]

    def blocked_pos(self, pos):
        if self.pixel(pos):
            return True
        return False

    def blocked(self, from_u, to_v=None):
        blocks = []
        if isinstance(from_u, Node):
            from_u = from_u.pos
        if self.blocked_pos(from_u):
            blocks.append(from_u)
        if isinstance(to_v, Node):
            to_v = to_v.pos
        if to_v is None:
            return blocks
        if self.blocked_pos(to_v):
            blocks.append(to_v)
        diff_h = to_v[0] - from_u[0]
        diff_w = to_v[1] - from_u[1]
        steps = int(max(abs(diff_w), abs(diff_h)) / 1)
        dh = diff_h / (steps + 0.00000001)
        dw = diff_w / (steps + 0.00000001)
        for step in range(steps):
            _h = int(from_u[0] + (step + 1) * dh)
            _w = int(from_u[1] + (step + 1) * dw)
            if self.blocked_pos((_h, _w)):
                blocks.append((_h, _w))
        uni_set = set(blocks)
        blocks = list(uni_set)
        return blocks

    def weight(self, from_u, to_v):
        # print("46, from to", from_u, to_v)
        if isinstance(from_u, Node):
            from_u = from_u.pos
        if isinstance(to_v, Node):
            to_v = to_v.pos
        if from_u is None or to_v is None:
            return float('inf')
        if not self.check_pos(from_u):
            return float('inf')
        if not self.check_pos(to_v):
            return float('inf')
        blocks = self.blocked(from_u, to_v)
        if blocks:
            return float('inf')
        dist = self.distance(from_u, to_v, 'Euler')
        return dist 

    def cost(self, from_u, to_v):
        if isinstance(from_u, Node):
            from_u = from_u.pos
        if isinstance(to_v, Node):
            to_v = to_v.pos
        return self.weight(from_u, to_v)

    def distance(self, from_u, to_v, mode='Euler'):
        if isinstance(from_u, Node):
            from_u = from_u.pos
        if isinstance(to_v, Node):
            to_v = to_v.pos
        if mode == 'Block':
            return abs(from_u[0] - to_v[0]) + abs(from_u[1] - to_v[1])
        else: #'Euler'
            return ((from_u[0] - to_v[0]) ** 2 + (from_u[1] - to_v[1]) ** 2) ** 0.5

    def check_pos(self, pos):
        # print("66 pos", pos)
        if pos[0] < 0 or pos[0] >= self.height:
            return False
        if pos[1] < 0 or pos[1] >=self.width:
            return False
        return True

    def heuristic(self, from_u, to_v, mode='Euler'): #欧拉距离 Euler, 街区距离 Block
        if isinstance(from_u, Node):
            from_u = from_u.pos
        if isinstance(to_v, Node):
            to_v = to_v.pos
        if not self.check_pos(from_u):
            return float('inf')
        if not self.check_pos(to_v):
            return float('inf')
        return self.distance(from_u, to_v, mode)

    def neighbours(self, pos): 
        nbs = []
        for i in [-1, 0, 1]:
            for j in [-1, 0, 1]:
                if i == 0 and j == 0:
                    continue
                if self.check_pos((pos[0] + i, pos[1] + j)):
                    nbs.append((pos[0] + i, pos[1] + j))
        return nbs
    
    def successor(self, pos):
        return self.neighbours(pos)
    
    def position(self, nid):
        return (nid[0], nid[1])

    def load(self, map_file):
        self.raw_map = img.imread(map_file)
        self.pixels = 1 - self.raw_map[:,:,0] #1是障碍，0是可行区
        self.height = len(self.pixels)
        self.width = len(self.pixels[0])

    @property
    def displayer(self):
        if not hasattr(self, "_displayer"):
            self._displayer = PyplotDisplayer()
        return self._displayer

    @displayer.setter
    def displayer(self, displayer):
        self._displayer = displayer

    def draw(self, displayer=None):
        if displayer is not None:
            self.displayer = displayer
        self.displayer.imshow(self.raw_map, cmap='Greys_r')

    def savefig(self, fname=None, displayer=None):
        if displayer is not None:
            self.displayer = displayer
        self.displayer.savefig(fname=fname)


if __name__ == "__main__":
    pass